// midiparser.h

// 2008-04-21 21:26:22 (Jake): created

#ifndef midiparser_h
#define midiparser_h

#include <string>
#include <map>

class Buffer {
public:
	Buffer();
	~Buffer();
	void read(const std::string& filename);
	void destroy();

	int getSize() { return _size; }
	
	void setPos(int pos) {
		_pos = pos;
	}

	void advance(int num) {
		if (_pos + num < _size) {
			_pos += num;
		}
		else {
			throw std::runtime_error("advance outside buffer");
		}
	}

	unsigned int getPos() { return _pos; }
	int getNumBytesLeft() { return _size - _pos; }

	unsigned char read() {
		if (_pos < _size) {
			return _buf[_pos++];
		}

		throw std::runtime_error("read outside buffer");
	}

	unsigned char peek() {
		return _buf[_pos];
	}

	unsigned char* read(int length) {
		if (length == 0) {
			return 0;
		}
		else if (_pos + length < _size) {
			unsigned char* bufstart = _buf + _pos;
			_pos += length;
			return bufstart;
		}

		throw std::runtime_error("read outside buffer");
	}

protected:
	unsigned char* _buf;
	unsigned int _size;
	unsigned int _pos;
};

struct MidiChunkHeader {
//	char id[4];
	std::string id;
	unsigned int length;

	void read(Buffer& buffer);
/*
	std::string toString() {
		std::ostringstream os;
		os << id << " " << length;
		return os.str();
	}
*/
};

class MidiChunk {
public:
	MidiChunkHeader header;

	virtual ~MidiChunk() { }
};

class MidiChunk_MThd : public MidiChunk {
public:
	unsigned short type;
	unsigned short numtracks;
	unsigned short division;
};

class MidiChunk_MTrk : public MidiChunk {
	
};

class MidiOutput {
public: 
	virtual void header(int type, int numTracks, unsigned char b1, unsigned char b0) { }
	virtual void beginTrack(int track, int size) { }
	virtual void endTrack() { }
	virtual void relTime(int t) { }
	virtual void eventNoteOn(int channel, int note, int velocity) { }
	virtual void eventNoteOff(int channel, int note, int velocity) { }
//	virtual void metaEvent(int code, unsigned char* data, int length) { }
	virtual void tempo(int tempo) { }

};

class MidiParser {
public:
	int readHeader(const std::string& filename, MidiOutput& output);
	void readHeaderAndTracks(const std::string& filename, MidiOutput& output);

protected:
	unsigned int readVarLen();
	
	unsigned char readByte() { 
		return _buffer.read(); 
	}
	
	unsigned short readShort() { 
		unsigned char* bytes = _buffer.read(2); 
		return (bytes[0] << 8) | bytes[1];
	}

	unsigned long readLong() { 
		unsigned char* bytes = _buffer.read(4); 
		return (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
	}

	void read(MidiChunkHeader& header);
	void MidiParser::readTrack(int trackNum, MidiOutput& output);

	Buffer _buffer;
	int _time;
	int _status;
};


#endif